#include "component_font_awesome.h"
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H

struct _ComponentFontAwesome
{
    GtkWidget parent_instance;
    FT_ULong charcode;
    gchar *color;
    gchar *bgcolor;
    unsigned int size;
};

enum
{
    PROP_0,
    PROP_CHARCODE,
    PROP_COLOR,
    PROP_BGCOLOR,
    PROP_SIZE,
    N_PROPERTIES
};

G_DEFINE_TYPE(ComponentFontAwesome, component_font_awesome, GTK_TYPE_WIDGET)
static GParamSpec *obj_properties[N_PROPERTIES] = {
    NULL,
};

static void component_font_awesome_measure(GtkWidget *widget, GtkOrientation orientation, int for_size, int *minimum, int *natural, int *minimum_baseline, int *natural_baseline)
{
    ComponentFontAwesome *self = COMPONENT_FONT_AWESOME(widget);

    *minimum = self->size;
    *natural = self->size;
}

static GdkPixbuf *ft_bitmap_to_pixbuf(const FT_Bitmap *bitmap, GdkRGBA *color)
{
    int width = bitmap->width;
    int height = bitmap->rows;

    GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
    guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
    int stride = gdk_pixbuf_get_rowstride(pixbuf);

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            int index = y * width + x;
            guchar value = bitmap->buffer[index];
            pixels[y * stride + x * 4 + 0] = value * color->red;   // Red
            pixels[y * stride + x * 4 + 1] = value * color->green; // Green
            pixels[y * stride + x * 4 + 2] = value * color->blue;  // Blue
            pixels[y * stride + x * 4 + 3] = value * color->alpha; // Alpha
        }
    }

    return pixbuf;
}

static void component_font_awesome_on_draw(GtkWidget *widget, GtkSnapshot *snapshot)
{
    ComponentFontAwesome *self = COMPONENT_FONT_AWESOME(widget);

    GBytes *font_data = g_resources_lookup_data("/src/assets/fonts/font_awesome.ttf", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
    gsize size;
    const gchar *data = g_bytes_get_data(font_data, &size);

    FT_Library library;
    FT_Init_FreeType(&library);
    FT_Face face;
    FT_New_Memory_Face(library, (const FT_Byte *)data, size, 0, &face);

    FT_Set_Pixel_Sizes(face, self->size, self->size);
    FT_UInt glyph_index = FT_Get_Char_Index(face, self->charcode);
    FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
    FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    GdkRGBA color;
    gdk_rgba_parse(&color, self->color);
    GdkPixbuf *pixbuf = ft_bitmap_to_pixbuf(&face->glyph->bitmap, &color);
    GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf);
    int texture_width = gdk_texture_get_width(texture);
    int texture_height = gdk_texture_get_height(texture);

    int offset_x = (self->size - texture_width) / 2;
    int offset_y = (self->size - texture_height) / 2;
    gtk_snapshot_append_texture(snapshot, texture, &GRAPHENE_RECT_INIT(offset_x, offset_y, texture_width, texture_height));

    FT_Done_Face(face);
    FT_Done_FreeType(library);
    g_bytes_unref(font_data);
    g_object_unref(pixbuf);
    g_object_unref(texture);
}

static void component_font_awesome_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
    ComponentFontAwesome *self = COMPONENT_FONT_AWESOME(object);

    switch (property_id)
    {
    case PROP_CHARCODE:
        self->charcode = g_value_get_ulong(value);
        gtk_widget_queue_draw(GTK_WIDGET(self));
        break;
    case PROP_COLOR:
        self->color = g_value_dup_string(value);
        gtk_widget_queue_draw(GTK_WIDGET(self));
        break;
    case PROP_BGCOLOR:
        self->bgcolor = g_value_dup_string(value);
        gtk_widget_queue_draw(GTK_WIDGET(self));
        break;
    case PROP_SIZE:
        self->size = g_value_get_uint(value);
        gtk_widget_queue_draw(GTK_WIDGET(self));
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        return;
    }
}

static void component_font_awesome_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
    ComponentFontAwesome *self = COMPONENT_FONT_AWESOME(object);

    switch (property_id)
    {
    case PROP_CHARCODE:
        g_value_set_ulong(value, self->charcode);
        break;
    case PROP_COLOR:
        g_value_set_string(value, self->color);
        break;
    case PROP_BGCOLOR:
        g_value_set_string(value, self->bgcolor);
        break;
    case PROP_SIZE:
        g_value_set_uint(value, self->size);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
        break;
    }
}

static void component_font_awesome_class_init(ComponentFontAwesomeClass *klass)
{
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    widget_class->snapshot = component_font_awesome_on_draw;
    widget_class->measure = component_font_awesome_measure;

    GObjectClass *object_class = G_OBJECT_CLASS(klass);
    object_class->set_property = component_font_awesome_set_property;
    object_class->get_property = component_font_awesome_get_property;

    obj_properties[PROP_CHARCODE] = g_param_spec_ulong("charcode", "Charcode", "Charcode", 0, G_MAXULONG, 0, G_PARAM_READWRITE);
    obj_properties[PROP_COLOR] = g_param_spec_string("color", "Color", "Color", "black", G_PARAM_READWRITE);
    obj_properties[PROP_BGCOLOR] = g_param_spec_string("bgcolor", "Bgcolor", "Background Color", "white", G_PARAM_READWRITE);
    obj_properties[PROP_SIZE] = g_param_spec_uint("size", "Size", "Size", 0, G_MAXUINT, 0, G_PARAM_READWRITE);

    g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
}

static void component_font_awesome_init(ComponentFontAwesome *self)
{
}
